<!doctype html>
<html lang="zh-cn">
<head>
  <meta charset="utf-8" />
  <title>按城市/明星预测票价（折线图）</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <style>
    body{font-family:Arial, Helvetica, sans-serif;margin:16px}
    .controls{margin-bottom:12px}
    select{padding:6px;margin-right:8px}
    #chart{width:100%;height:600px;border:1px solid #eee}
    .hint{color:#666;font-size:13px;margin-top:8px}
  </style>
</head>
<body>
  <h3>基于城市与明星预测下一场演唱会最低/最高价（折线图）</h3>

  <div class="controls">
    城市：
    <select id="citySelect"><option>加载中...</option></select>
    明星 / 用户：
    <select id="userSelect"><option>请选择城市</option></select>
    <button id="btnDraw">绘制折线图</button>
  </div>

  <div id="chart"></div>
  <div class="hint">说明：横轴为场次时间（start_date_str），最后一点为预测（若存在）。若 predict_result.csv 未能读取，请把 predict_result.csv 放到可被静态服务访问的位置（例如项目根或 static 目录）。</div>

  <!-- 使用本项目内的 echarts -->
  <script src="/static/js/echarts.min.js"></script>
  <!-- 使用 PapaParse 来解析 CSV（若无网络可把 papaparse.js 放到 static 并修改引用）-->
  <script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>

  <script>
  (function(){
    const csvPath = '/static/predict_result.csv'; // 若模板路径不同，可调整为相对路径或把 csv 放入 static
    let allRows = [];

    const citySel = document.getElementById('citySelect');
    const userSel = document.getElementById('userSelect');
    const btn = document.getElementById('btnDraw');

    function parseBool(v){
      return (String(v).toLowerCase()==='true') || v === true;
    }

    function safeNum(v){
      const n = parseFloat(v);
      return isNaN(n) ? null : n;
    }

    function populateCity(){
      const cities = Array.from(new Set(allRows.map(r=>r.city).filter(Boolean))).sort();
      citySel.innerHTML = '<option value="">-- 选择城市 --</option>' + cities.map(c=>`<option value="${c}">${c}</option>`).join('');
      userSel.innerHTML = '<option value="">请选择城市</option>';
    }

    function populateUsers(city){
      const users = Array.from(new Set(allRows.filter(r=>r.city===city).map(r=>r.users || '未知'))).sort();
      userSel.innerHTML = '<option value="">-- 选择明星/用户 --</option>' + users.map(u=>`<option value="${u}">${u}</option>`).join('');
    }

    function normalizeRows(rows){
      return rows.map(r=>{
        // 清洗字段
        return {
          city: r.city || '',
          users: r.users || '',
          title: r.title || '',
          start_date_str: r.start_date_str || r.start_date || r.time || '',
          time: r.time || '',
          min: safeNum(r.min),
          max: safeNum(r.max),
          is_predicted: parseBool(r.is_predicted)
        };
      });
    }

    function sortByDateThenPred(rows){
      // 若 start_date_str 可以 parse 成日期则按日期升序，预测行放最后（但仍保留在序列中）
      function toTimestamp(s){
        const d = Date.parse(s);
        return isNaN(d) ? null : d;
      }
      return rows.slice().sort((a,b)=>{
        if (a.is_predicted && !b.is_predicted) return 1;
        if (!a.is_predicted && b.is_predicted) return -1;
        const ta = toTimestamp(a.start_date_str);
        const tb = toTimestamp(b.start_date_str);
        if (ta==null && tb==null) return 0;
        if (ta==null) return 1;
        if (tb==null) return -1;
        return ta - tb;
      });
    }

    function drawChart(filtered){
      const container = document.getElementById('chart');
      const myChart = echarts.init(container);
      const data = sortByDateThenPred(filtered);
      const xLabels = data.map(d=>d.start_date_str || '');
      const minVals = data.map(d=>d.min==null?'-':d.min);
      const maxVals = data.map(d=>d.max==null?'-':d.max);

      // 找到预测点索引
      const predictedIdxs = data.map((d,i)=>d.is_predicted?i:-1).filter(i=>i>=0);

      const option = {
        tooltip: {trigger:'axis'},
        legend: {data:['最低价','最高价'], top:8},
        toolbox: {feature:{saveAsImage:{}} },
        grid: {left:50,right:20,top:60,bottom:80},
        xAxis: {
          type: 'category',
          data: xLabels,
          axisLabel: {rotate:30,interval:0}
        },
        yAxis: {type:'value', name:'价格 (元)'},
        series: [
          {
            name:'最低价',
            type:'line',
            data:minVals,
            smooth:true,
            lineStyle:{width:2},
            // 对预测点用明显标记
            markPoint: {
              data: predictedIdxs.map(i=>{
                return {
                  name:'预测最低',
                  coord:[xLabels[i], minVals[i]],
                  value: minVals[i],
                  itemStyle: {color:'#d14a61'}, // 红色标记
                  label: {formatter: '预测\n{c}', position:'top'}
                };
              })
            },
            // 在预测处画竖直虚线（基于 xAxis 类目）
            markLine: {
              silent: true,
              data: predictedIdxs.map(i=>({xAxis: xLabels[i], name:'预测'})),
              lineStyle: {type:'dashed', color:'#999'}
            }
          },
          {
            name:'最高价',
            type:'line',
            data:maxVals,
            smooth:true,
            lineStyle:{width:2},
            markPoint: {
              data: predictedIdxs.map(i=>{
                return {
                  name:'预测最高',
                  coord:[xLabels[i], maxVals[i]],
                  value: maxVals[i],
                  itemStyle: {color:'#2f4554'}, // 不同颜色
                  label: {formatter: '预测\n{c}', position:'bottom'}
                };
              })
            }
          }
        ]
      };

      myChart.setOption(option);
      window.addEventListener('resize', ()=> myChart.resize());
    }

    // 读取 CSV
    Papa.parse(csvPath, {
      download: true,
      header: true,
      skipEmptyLines: true,
      complete: function(results){
        allRows = normalizeRows(results.data);
        populateCity();
      },
      error: function(err){
        citySel.innerHTML = '<option>读取 predict_result.csv 失败，请确认文件路径可被访问</option>';
        console.error(err);
      }
    });

    citySel.addEventListener('change', function(){
      const city = this.value;
      if (!city) { userSel.innerHTML = '<option value="">请选择城市</option>'; return; }
      populateUsers(city);
    });

    btn.addEventListener('click', function(){
      const city = citySel.value;
      const user = userSel.value;
      if (!city || !user){ alert('请选择城市和明星/用户'); return; }
      const filtered = allRows.filter(r=>r.city===city && r.users===user);
      if (filtered.length===0){ alert('未找到匹配记录'); return; }
      drawChart(filtered);
    });

  })();
  </script>
</body>
</html>